package ks.server.controllers.commands.admin.plugin;

import ks.framework.common.Message;
import ks.framework.communicator.Communicator;
import ks.framework.interfaces.IProcessMessage;
import ks.managers.plugin.Plugin;
import ks.managers.plugin.PluginManager;
import ks.server.ServerContext;
import ks.server.controllers.requests.AckRequest;
import ks.server.controllers.requests.NackRequest;
import ks.server.ipc.Listener;
import ks.server.persist.PersistentPluginManager;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import java.io.File;
import java.net.MalformedURLException;


/**
 * Server-side controller for processing an admin-command to remove a plugin
 * @author harry moreno
 *
 */
public class PluginUninstallController implements IProcessMessage {

	/** Primary agent to whom we can communicate our messages. */
	Communicator com;

	/** regular expression indicating how to divide xml */
	String delims = "[/.]+";	//divides string by '/' and '.'

	/** Plugin directory where we will dump all plugins retrieved remotely. */
	public static final String pluginStorage = "Plugins";

	/** Attributes from XML string. */
	public static final String pluginAtt = "plugin";

	public PluginUninstallController (Communicator com) {
		this.com = com;
	}
	/** 
	 * plugin-uninstall Message comes in from Client.
	 * 
	 *  <plugin-uninstall plugin="Heineman_Klondike"/>
	 * @throws MalformedURLException 
	 */
	public boolean process(Message m){
		Node pluginUninstall = m.contents();

		// Find out plugins name
		NamedNodeMap nnp = pluginUninstall.getAttributes();

		Node pluginNode = nnp.getNamedItem(pluginAtt);
		String pluginName = pluginNode.getNodeValue();

		// See if we can remove the jar file. If we can't stop now.
		if (!removeJar(pluginName)) {
			Listener.nack(m, 4, "Unable to remove plugin from directory");
			return false;
		}

		// Ask PluginManager to remove the plugin.
		try {
			PersistentPluginManager pm = ServerContext.getPluginManager();

			if (pm.remove(pm.getPlugin(pluginName))) {
				// successful operation!
			} else {
				new NackRequest(com).send(m, 4, "Unable to remove plugin.", m.getOriginator());
			}

			// Broadcast plugins to everyone. Ask PluginManager for collection of plugins, 
			// and form this request from this set. This is the most upto date list of plugins.
			StringBuilder sb = new StringBuilder("<request version='1.0' id='1'><plugins>");
			for (Plugin plugin : pm.getList()) {
				sb.append ("<plugin name=\"" + plugin.name + "\" uri=\"" + plugin.location + "\"/>\n");
			}
			sb.append("</plugins></request>");
			Document d = Message.construct(sb.toString());

			// send to everyone INCLUDING the originator of the Message m.
			Message r = new Message (d);
			r.setRecipient(m.getOriginator());
			r.setBroadcast();

			com.distribute(r);
			new AckRequest(com).send(m, "Success.", m.getOriginator());
			return true;
		} catch (Exception e) {
			e.printStackTrace();

			return false;
		}
	}
	/**
	 * Try to remove the plugin from our local storage.
	 * You only need to pass this function the name of the plugin to be removed.
	 * modeled after code snipit: http://www.java2s.com/Code/Java/File-Input-Output/DeletefileusingJavaIOAPI.htm
	 * @param pluginName name given to plugin
	 * @return boolean
	 */
	private boolean removeJar(String pluginName) {

		try {
			String fileName = pluginName + ".jar";
			File f = new File(pluginStorage, fileName);


			// Make sure the file or directory exists and isn't write protected
			if (!f.exists())
				throw new IllegalArgumentException(
						"Delete: no such file or directory: " + fileName);

			if (!f.canWrite())
				throw new IllegalArgumentException("Delete: write protected: "
						+ fileName);

			// If it is a directory, make sure it is empty
			if (f.isDirectory()) {
				throw new IllegalArgumentException(
							"Delete: directory indicated, did you pass a plugin Name?: " + fileName);
			}

			// Attempt to delete it
			f.delete();

		} catch (Exception e) {
			// unable to store or create
			return false;
		}
		
		// done
		return true;
	}
}
